#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <mutex>
#include <jsoncpp/json/json.h>

#include "oj_model.hpp"
#include "oj_view.hpp"
#include "../comm/log.hpp"
#include "../comm/util.hpp"
#include "../comm/httplib.h"
#include "./LoadBalance.hpp"

namespace ns_control
{
    using namespace ns_model;
    using namespace ns_view;
    using namespace ns_LoadBalance;

    using namespace ns_log;
    using namespace ns_util;
    using namespace httplib;

    class control
    {
    public:
        model _model;             // 提供后台数据
        view _view;               // 提供渲染功能
        loadbalance _loadbalance; // 负载均衡器

    public:
        control()
        {
        }
        ~control()
        {
        }

    public:
        bool AllQuestions(std::string *html)
        {
            std::vector<question> v;
            if (_model.GetAllQuestions(&v))
            {
                _view.AllExpandHtml(v, html);
                return true;
            }
            else
            {
                *html = "[获取题目失败，形成题目列表失败]";
                return false;
            }
        }
        bool OneQuestion(const std::string &number, std::string *html)
        {
            question q;
            if (_model.GetOneQuestion(number, &q))
            {
                _view.OneExpandHtml(q, html);
                return true;
            }
            else
            {
                *html = "[获取第" + number + "题目信息列表失败]";
                return false;
            }
        }

    public:
        /**/
        void judge(const std::string &number, const std::string in_json, std::string *out_json)
        {
            /********************************************************
             * 1.in_json进行反序列化，得到题目id，得到用户提交的源代码，input；
             * 2.重新拼接用户代码+测试用例代码，形成新的代码；
             * 3.选择负载最低的主机，然后发起http请求，得到结果；
             * 4.将结果赋值给out_json
             ********************************************************/
            struct question q;
            _model.GetOneQuestion(number, &q);

            Json::Reader reader;
            Json::Value in_value;
            reader.parse(in_json, in_value);
            std::string code = in_value["code"].asString();

            Json::Value compile_value;
            compile_value["input"] = in_value["input"].asString();
            compile_value["code"] = code + q.tail;
            compile_value["cpu_limit"] = q.cpu_limit;
            compile_value["mem_limit"] = q.mem_limit;
            Json::FastWriter writer;
            std::string compile_string = writer.write(compile_value);

            while (1)
            {
                int id = 0;
                Machine *m = nullptr;
                if (!_loadbalance.smartchoice(&id, &m))
                {
                    break;
                }
                LOG(INFO) << "选择主机成功,主机id:" << id << "详情:" << m->_ip << ":" << m->_port << "\n";

                Client cli(m->_ip, m->_port);
                m->increase_load();
                if (auto res = cli.Post("/compile_and_run", compile_string, "application/json;charset=utf-8"))
                {
                    if (res->status == 200)
                    {
                        *out_json = res->body;
                        m->decrease_load();
                        LOG(INFO) << "后端编译与运行服务成功..." << "\n";
                        break;
                    }
                    m->decrease_load();
                }
                else
                {
                    LOG(ERROR) << "当前待请求的主机id:" << id << "详情:" << m->_ip << ":" << m->_port << "可能已经离线" << "\n";
                    m->increase_load();
                    _loadbalance.offlinemachine(id);
                }
            }
        }
    };
}
